home *** CD-ROM | disk | FTP | other *** search
/ SGI Hot Mix 17 / Hot Mix 17.iso / HM17_SGI / research / lib / read_sylk.pro < prev    next >
Text File  |  1997-07-08  |  18KB  |  547 lines

  1. ; $Id: read_sylk.pro,v 1.5 1997/01/15 03:11:50 ali Exp $
  2. ;
  3. ; Copyright (c) 1994-1997, Research Systems, Inc.  All rights reserved.
  4. ;       Unauthorized reproduction prohibited.
  5.  
  6.  
  7. ; Returns TRUE if TestVal is in the range of LoVal through HiVal
  8.  
  9. FUNCTION ContainsVal, LoVal, TestVal, HiVal
  10.  
  11.     RETURN, ((LoVal LE TestVal) AND (TestVal LE HiVal))
  12. END
  13.  
  14.  
  15. ; Scans the passed sylk data line and returns the new cell row.
  16.  
  17. FUNCTION GetSylkCellRow, szCellLine, iCurRow
  18.  
  19.     ON_ERROR, 2
  20.  
  21.     iRow = 0
  22.     posRowData = STRPOS(szCellLine, ";Y")
  23.  
  24.     IF (posRowData NE -1) THEN BEGIN
  25.     READS, STRMID(szCellLine, posRowData + 2, $
  26.         STRLEN(szCellLine) - posRowData), iRow, FORMAT = "(I)"
  27.     ENDIF
  28.     RETURN, iRow - 1
  29. END
  30.  
  31.  
  32. ; Scans the current sylk data line and returns the current cell column.
  33.  
  34. FUNCTION GetSylkCellCol, szCellLine
  35.  
  36.     ON_ERROR, 2
  37.  
  38.     iCol = 0
  39.     posColData = STRPOS(szCellLine, ";X")
  40.  
  41.     IF (posColData NE -1) THEN BEGIN
  42.         READS, STRMID(szCellLine, posColData + 2, $
  43.             STRLEN(szCellLine) - posColData), iCol, FORMAT = "(I)"
  44.     ENDIF
  45.     RETURN, iCol - 1
  46. END
  47.  
  48.  
  49. ; Reads in the entire sylk file and determines the complete range of cell data 
  50. ; therein.
  51.  
  52. FUNCTION GetSylkCellRange, lunFile, strCellRange
  53.  
  54.     ON_ERROR, 2
  55.  
  56.     szFileLine = ""
  57.     iCurRow = 0
  58.     iCurCol = 0
  59.     iFirstRow = 32767
  60.     iFirstCol = 32767
  61.     iLastRow = 0
  62.     iLastCol = 0
  63.  
  64.     WHILE (NOT EOF(lunFile)) DO BEGIN
  65.         READF, lunFile, szFileLine
  66.  
  67.         iCurRow = GetSylkCellRow(szFileLine)
  68.         IF (iCurRow NE -1) THEN BEGIN
  69.             iFirstRow = (iCurRow < iFirstRow)
  70.             iLastRow = (iCurRow > iLastRow)
  71.         ENDIF
  72.  
  73.         iCurCol = GetSylkCellCol(szFileLine)
  74.         IF (iCurCol NE -1) THEN BEGIN
  75.             iFirstCol = (iCurCol < iFirstCol)
  76.             iLastCol = (iCurCol > iLastCol)
  77.         ENDIF
  78.     ENDWHILE
  79.  
  80.     POINT_LUN, lunFile, 0
  81.  
  82.     strCellRange.(0) = iFirstRow
  83.     strCellRange.(1) = iFirstCol
  84.     strCellRange.(2) = iLastRow
  85.     strCellRange.(3) = iLastCol
  86.  
  87.     RETURN, ((iLastRow - iFirstRow GT 0) OR (iLastCol - iFirstCol GT 0))
  88. END
  89.  
  90.  
  91. ; Reads in sylk cell data line, determines the type of data in the cell and
  92. ; returns that data.
  93.  
  94. FUNCTION GetSylkCellContents, szFileLine, fUseLongs, fUseDoubles
  95.  
  96.     ON_ERROR, 2
  97.  
  98.     mdefCellContents = 0B
  99.     szCellContents = ""
  100.     posCellContents = STRPOS(szFileLine, ";K")
  101.     
  102.     IF (posCellContents NE -1) THEN BEGIN
  103.         szCellContents = STRMID(szFileLine, posCellContents + 2, $ 
  104.             STRLEN(szFileLine) - posCellContents)
  105.  
  106.         szCellContents = szCellContents + ";"
  107.  
  108.         ; Check to see if it's a string or not.  If it is, it will have a '"'
  109.         ; as the first character.
  110.         IF (STRPOS(szCellContents, 34B) EQ 0) THEN BEGIN
  111.  
  112.             ; Find the end of the cell's text contents, which will be the last
  113.             ; '"' in the string before the next ";", and extract that text from 
  114.             ; the string.
  115.             mdefCellContents = STRMID(szCellContents, 1, $
  116.             STRPOS(szCellContents, ";", 1) - 2)
  117.                 
  118.         ; Check to see if it's a floating point value.  If it is, it will have a
  119.         ; "." in the string before the next ";" delimeter.
  120.         ENDIF ELSE BEGIN
  121.             posPeriod = STRPOS(szCellContents, ".")
  122.             IF ((posPeriod GT -1) AND $
  123.                 (posPeriod LT STRPOS(szCellContents, ";"))) THEN BEGIN
  124.  
  125.                 ; If the user wishes to use double precision, return a DOUBLE.
  126.                 IF (fUseDoubles) THEN BEGIN
  127.                     mdefCellContents = 0.0D
  128.                     READS, szCellContents, mdefCellContents, FORMAT = "(D)"
  129.                 
  130.                 ; Otherwise, return a FLOAT.
  131.                 ENDIF ELSE BEGIN
  132.                     mdefCellContents = 0.0
  133.                     READS, szCellContents, mdefCellContents, FORMAT = "(F)"
  134.                 ENDELSE
  135.     
  136.             ; Otherwise, the sylk cell data must be an integer type.
  137.             ENDIF ELSE BEGIN
  138.         
  139.                 ; If the user wishes to use long ints, return a LONG.
  140.                 IF (fUseLongs) THEN BEGIN
  141.                     mdefCellContents = 0L
  142.  
  143.                 ; Otherwise, return an INT.
  144.                 ENDIF ELSE BEGIN
  145.                     mdefCellContents = 0
  146.                 ENDELSE
  147.  
  148.                 READS, szCellContents, mdefCellContents, FORMAT = "(I)"
  149.  
  150.             ENDELSE
  151.         ENDELSE
  152.     ENDIF
  153.  
  154.     RETURN, mdefCellContents
  155. END
  156.  
  157.  
  158. FUNCTION READ_SYLK, Infile, STARTROW = iStartRow, STARTCOL = iStartCol, $
  159.     NROWS = nRows, NCOLS = nCols, ARRAY = fArray, COLMAJOR = fColMajor, $
  160.     USEDOUBLES = fUseDoubles, USELONGS = fUseLongs 
  161. ;
  162. ;+
  163. ; NAME:
  164. ;   READ_SYLK
  165. ;
  166. ; PURPOSE:
  167. ;   Reads the contents of a sylk (Symbolic Link) format spreadsheet data file 
  168. ;   and returns a cell data range (if present) in the form of an IDL variable.
  169. ;
  170. ; CATEGORY:
  171. ;   Input/Output.
  172. ;
  173. ; CALLING SEQUENCE:
  174. ;   ReturnData = READ_SYLK(InFile [, STARTROW, STARTCOL, NROWS, NCOLS, ARRAY, 
  175. ;       COLMAJOR, USEDOUBLES, USELONGS])
  176. ;
  177. ; INPUT:
  178. ;   InFile: Scalar string with the name of the sylk file to read.
  179. ;
  180. ; OUTPUT:
  181. ;   ReturnData: The table (vector of structs) or matrix (2D array) that will
  182. ;       contain the spreadsheet cell data.  The size and type of this return
  183. ;       variable is set using the optional input parameters (keywords) below.
  184. ;
  185. ; OPTIONAL INPUT PARAMETERS:
  186. ;   STARTROW: The starting (0-based) row of spreadsheet cells to read.  If not
  187. ;       specified, this value defaults to the first row of cells found in the 
  188. ;       file.
  189. ;   STARTCOL: The starting (0-based) column of spreadsheet cells to read.  If 
  190. ;       not specified, this value defaults to the first column of cells found 
  191. ;       in the file.
  192. ;   NROWS: The number of spreadsheet rows to read in.  If not specified, this
  193. ;       defaults to all of the cell rows found in the file.
  194. ;   NCOLS: The number of spreadsheet columns to read in.  If not specified,
  195. ;       this value defaults to all of the cell columns found in the file.
  196. ;   ARRAY: Boolean flag.  If TRUE, the data type returned will be an IDL array.
  197. ;       Note that the data in the cell range must be of the same type to 
  198. ;       successfully return an array.  If this flag is not set, the routine
  199. ;       will return a table (vector of structs) instead.  The tags of this
  200. ;       struct will be labelled "Col0", "Col1", ..., "ColN" for a row major
  201. ;       table and "Row0", "Row1", ..., "RowN" for a column major table.
  202. ;   COLMAJOR: Boolean flag.  If TRUE, the range of spreadsheet cell data is
  203. ;       transposed and then read into an IDL variable.  This flag should be set 
  204. ;       when importing spreadsheet data which has column major organization 
  205. ;       (ie., listings by column rather than row).  The default is row major 
  206. ;       format.  
  207. ;   USEDOUBLES: Boolean flag.  If TRUE, any floating point cell data will be
  208. ;       read in and returned as a double precision rather than the default 
  209. ;       float type.
  210. ;   USELONGS: Boolean flag.  If TRUE, any integer cell data will be read in and
  211. ;       returned as a long rather than the default int type.
  212. ;
  213. ; SIDE EFFECTS:
  214. ;   None.
  215. ;
  216. ; RESTRICTIONS:
  217. ;   This routine *only* reads in numerical and string sylk data.  It igonores 
  218. ;   all spreadsheet and cell formatting information such as cell width, text 
  219. ;   justification, font type, date, time, and monetary notations, etc.  In
  220. ;   addition, only attempts to read spreadsheet tables, like-typed cell rows,
  221. ;   columns, or subsets thereof will succeed.
  222. ;
  223. ;
  224. ; EXAMPLES:
  225. ;   Consider the following row major spreadsheet table with the upper left cell
  226. ;   (value = "Index") at location [0, 0] that has been output to the sylk file
  227. ;   "foo.slk":
  228. ;   
  229. ;   Index   Name   Gender  Platform
  230. ;     1     Beth     F     Unix
  231. ;     2     Kirk     M     VMS
  232. ;     3     Mark     M     Windows
  233. ;     4     Dave     M     Macintosh
  234. ;
  235. ;   Note that the data format of the title row (STRING, STRING, STRING, STRING)
  236. ;   is inconsistant with the following four rows (INT, STRING, STRING, STRING)
  237. ;   in the table.   It is impossible to read all of the table into a single IDL
  238. ;   variable, but you could make two calls to READ_SYLK to import all of the
  239. ;   data:
  240. ;
  241. ;       strTitle = READ_SYLK("foo.slk", NROWS = 1)
  242. ;       arrstrTable = READ_SYLK("foo.slk", STARTROW = 1)
  243. ;
  244. ;   The return data are as follows:
  245. ;
  246. ;       IDL> HELP, strTitle
  247. ;       STRTITLE        STRUCT    = -> <Anonymous> Array[1]
  248. ;
  249. ;       IDL> PRINT, strTitle        
  250. ;       { Index Name Gender Platform}
  251. ;
  252. ;       IDL> HELP, arrstrTable
  253. ;       ARRSTRTABLE     STRUCT    = -> <Anonymous> Array[4]
  254. ;
  255. ;       IDL> PRINT, arrstrTable
  256. ;       {       1 Beth F Unix}{       2 Kirk M VMS}{       3 Mark M 
  257. ;       Windows}{       4 Dave M Macintosh}
  258. ;
  259. ;
  260. ;   Further, consider the following call from the same sylk file:
  261. ;
  262. ;       arrszNames = READ_SYLK("foo.slk", /ARRAY, STARTROW = 1, STARTCOL = 1, $
  263. ;           NCOLS = 1)
  264. ;
  265. ;   The return data is now:
  266. ;
  267. ;       IDL> HELP, arrszNames
  268. ;       ARRSZTABLE      STRING    = Array[4]
  269. ;
  270. ;       IDL> PRINT, arrszNames
  271. ;       Beth Kirk Mark Dave 
  272. ;
  273. ;
  274. ;   If the COLMAJOR keyword flag is set the return value differs in type:
  275. ;
  276. ;       arrszNames = READ_SYLK("foo.slk", /ARRAY, /COLMAJOR, STARTROW = 1, $
  277. ;           STARTCOL = 1, NCOLS = 1)
  278. ;
  279. ;   The return data is now:
  280. ;
  281. ;       IDL> HELP, arrszNames
  282. ;       ARRSZTABLE      STRING    = Array[1, 4]
  283. ;
  284. ;       IDL> PRINT, arrszNames
  285. ;       Beth 
  286. ;       Kirk 
  287. ;       Mark 
  288. ;       Dave 
  289. ;
  290. ;
  291. ; MODIFICATION HISTORY:
  292. ;   Written October 1994, AJH
  293. ;   Converted from handles to pointers, 17 December 1996, AB
  294. ;-
  295. ; Copyright (c) 1994, Research Systems, Inc.  All rights reserved.
  296. ;   Unauthorized reproduction prohibited.
  297. ;
  298.  
  299.     ON_ERROR, 2
  300.     ON_IOERROR, CleanUp
  301.     
  302.     iCurRow = 0
  303.     iCurCol = 0
  304.     strCellRange = {iStartRow:0, iStartCol:0, iEndRow:0, iEndCol:0}
  305.     szFileLine = ""
  306.     lunInfile = 0
  307.     ReturnData = 0
  308.  
  309.     ; First check to see if the correct number of positional parameters have 
  310.     ; been passed.
  311.     IF (N_PARAMS() NE 1) THEN BEGIN
  312.         MESSAGE, "Calling sequence - ReturnData = READ_SYLK(Infile [, " + $
  313.             "STARTROW, STARTCOL, NROWS, NCOLS, ARRAY, COLMAJOR, " + $
  314.             "USEDOUBLES, USELONGS])", /CONTINUE
  315.         GOTO, CleanUp    
  316.     ENDIF
  317.  
  318.     ; Check for the validity of the file parameter
  319.     IF (N_ElEMENTS(Infile) EQ 0) THEN BEGIN
  320.         MESSAGE, "Error - A STRING filename must be passed in the Infile " + $
  321.             "parameter.", /CONTINUE
  322.         GOTO, CleanUp
  323.     ENDIF
  324.     
  325.     ; If Infile is a filename, open it for reading and get its lun
  326.     IF ((SIZE(Infile))[1] EQ 7) THEN BEGIN
  327.         OPENR, lunInfile, Infile, /GET_LUN, ERROR = fOpenRead
  328.         IF (fOpenRead NE 0) THEN BEGIN
  329.             MESSAGE, "Error - File " + STRCOMPRESS(Infile, /REMOVE_ALL) + $
  330.                 " cannot be opened.", /CONTINUE
  331.             GOTO, CleanUp
  332.         ENDIF
  333.         fstatResult = FSTAT(lunInfile)
  334.         IF (fstatResult.READ EQ 0) THEN BEGIN
  335.             MESSAGE, "Error - File with LUN of " + $
  336.                 STRCOMPRESS(STRING(lunInfile), /REMOVE_ALL) + $
  337.                 " cannot be read from.", /CONTINUE
  338.             GOTO, CleanUp
  339.         ENDIF
  340.     ENDIF
  341.  
  342.     ; Get the actual cell range from the file.
  343.     fResult = GetSylkCellRange(lunInfile, strCellRange)
  344.     IF (fResult EQ 0) THEN BEGIN
  345.         MESSAGE, "Error - there is no sylk cell data in the input file " + $
  346.             "specified.", /CONTINUE
  347.         GOTO, CleanUp
  348.     ENDIF
  349.  
  350.     ; Setup values for cell range based on keywords and actual range.
  351.     IF (N_ELEMENTS(iStartRow) EQ 0) THEN BEGIN
  352.         iStartRow = strCellRange.iStartRow
  353.     ENDIF ELSE BEGIN
  354.         iStartRow = (strCellRange.iStartRow > iStartRow)
  355.     ENDELSE
  356.  
  357.     IF (N_ELEMENTS(iStartCol) EQ 0) THEN BEGIN
  358.         iStartCol = strCellRange.iStartCol
  359.     ENDIF ELSE BEGIN
  360.         iStartCol = (strCellRange.iStartCol > iStartCol)
  361.     ENDELSE
  362.  
  363.     IF (N_ELEMENTS(nRows) EQ 0) THEN BEGIN
  364.         nRows = (strCellRange.iEndRow - iStartRow) + 1
  365.     ENDIF ELSE BEGIN
  366.         nRows = (1 > nRows)
  367.         nRows = ((strCellRange.iEndRow - iStartRow) + 1 < nRows)
  368.     ENDELSE
  369.  
  370.     IF (N_ELEMENTS(nCols) EQ 0) THEN BEGIN
  371.         nCols = (strCellRange.iEndCol - iStartCol) + 1
  372.     ENDIF ELSE BEGIN
  373.         nCols = (1 > nCols)
  374.         nCols = ((strCellRange.iEndCol - iStartCol) + 1 < nCols)
  375.     ENDELSE     
  376.  
  377.     ; Setup keyword boolean flags.
  378.     IF (N_ELEMENTS(fColMajor) EQ 0) THEN fColMajor = 0
  379.     IF (N_ELEMENTS(fArray) EQ 0) THEN fArray = 0
  380.     IF (N_ELEMENTS(fUseDoubles) EQ 0) THEN fUseDoubles = 0
  381.     IF (N_ELEMENTS(fUseLongs) EQ 0) THEN fUseLongs = 0
  382.  
  383.     ; Create a 2D array that will hold the IDs for the cell data pointers so
  384.     ; they can be easily referenced.
  385.     arrPointers = MAKE_ARRAY(nRows, nCols, /PTR)
  386.     
  387.     ; While not yet at the end of the input file, read in sylk data.
  388.     WHILE (NOT EOF(lunInfile)) DO BEGIN
  389.  
  390.         ; Read in a line of test from the sylk file.
  391.         READF, lunInfile, szFileLine
  392.  
  393.         ; Set the current row and column indeces.
  394.  
  395.         iRow = GetSylkCellRow(szFileLine)
  396.         IF (iRow NE -1) THEN BEGIN
  397.             iCurRow = iRow
  398.         ENDIF
  399.  
  400.         iCol = GetSylkCellCol(szFileLine)
  401.         IF  (iCol NE -1) THEN BEGIN
  402.             iCurCol = iCol
  403.         ENDIF
  404.         
  405.         ; Check to see if the file line contains cell data that is within range
  406.         ; of the sylk cell range desired.
  407.         IF (ContainsVal(iStartRow, iCurRow, iStartRow + nRows - 1) AND $
  408.             ContainsVal(iStartCol, iCurCol, iStartCol + nCols - 1) AND $
  409.             STRPOS(szFileLine, "C;") EQ 0) THEN BEGIN
  410.  
  411.             ; Create a pointer containing the data in the cell and put it's ID
  412.             ; into the array at it's row and column location.
  413.             arrPointers[iCurRow - iStartRow, iCurCol - iStartCol] = $
  414.            PTR_NEW(GetSylkCellContents(szFileLine, fUseLongs, fUseDoubles))
  415.  
  416.         ENDIF
  417.     ENDWHILE
  418.     
  419.     IF (fColMajor) THEN BEGIN
  420.         szTagPrefix = "Row"
  421.         arrPointers = TRANSPOSE(arrPointers)
  422.         iMax = nCols
  423.         jMax = nRows
  424.     ENDIF ELSE BEGIN
  425.         szTagPrefix = "Col"
  426.         iMax = nRows
  427.         jMax = nCols
  428.     ENDELSE
  429.  
  430.     ; Find the first valid pointer in the array of pointers, and determine
  431.     ; the data type to which it refers.
  432.     fIsValid = 0
  433.     i = -1
  434.  
  435.     nullPtr = PTR_NEW()
  436.     REPEAT BEGIN
  437.         i = i + 1
  438.         j = 0
  439.         REPEAT BEGIN
  440.             IF (arrPointers[i, j] NE nullPtr) THEN BEGIN
  441.                 fIsValid = 1
  442.         value = *(arrPointers[i, j])
  443.                 typeValue = (SIZE(value))[1]
  444.             ENDIF ELSE BEGIN
  445.                 j = j + 1
  446.             ENDELSE
  447.         END UNTIL ((j EQ jMax - 1) OR (fIsValid))
  448.     END UNTIL ((i EQ iMax - 1) OR (fIsValid))
  449.  
  450.     ; If there was no valid pointer in the array of pointers, make the return
  451.     ; data type bytes
  452.     IF (NOT fIsValid) THEN BEGIN
  453.         typeValue = 1
  454.     ENDIF
  455.  
  456.     ; If the user wants a matrix of the same type of data
  457.     IF (fArray) THEN BEGIN
  458.  
  459.         ; Create an array of that type.
  460.         ReturnData = MAKE_ARRAY(iMax, jMax, TYPE = typeValue)
  461.  
  462.         ; Fill the array.
  463.         FOR i = 0, iMax - 1 DO BEGIN
  464.             FOR j = 0, jMax - 1 DO BEGIN
  465.                 IF (arrPointers[i, j] NE nullPtr) THEN BEGIN
  466.                     value = *(arrPointers[i, j])
  467.                     IF ((SIZE(value))[1] NE typeValue) THEN BEGIN
  468.                         MESSAGE, "Error - Mixed data types found in cell " + $
  469.                             "range specified; unable to create and return " + $
  470.                             "an array.", /CONTINUE
  471.                         GOTO, CleanUp
  472.                     ENDIF
  473.                 ENDIF ELSE BEGIN
  474.                     value = 0B
  475.                 ENDELSE 
  476.                 ReturnData[i, j] = value
  477.             ENDFOR  
  478.         ENDFOR
  479.     
  480.     ; Otherwise, the user wants a table, so create a vector of structs in the
  481.     ; return variable.
  482.     ENDIF ELSE BEGIN
  483.         szTag = STRCOMPRESS(szTagPrefix + STRING(0), /REMOVE_ALL)
  484.         
  485.         ; Create the first tag in the structure.
  486.     value = *(arrPointers[i, j])
  487.         strData = CREATE_STRUCT(szTag, value)
  488.  
  489.         ; Step through each column in the array of pointers and create a tag
  490.         ; and appropriate type in the structure.
  491.         FOR j = 1, jMax - 1 DO BEGIN
  492.             szTag = STRCOMPRESS(szTagPrefix + STRING(j), /REMOVE_ALL)
  493.             IF (arrPointers[0, j] NE nullPtr) THEN BEGIN
  494.                 value = *(arrPointers[0, j])
  495.             ENDIF ELSE BEGIN
  496.                 value = 0B
  497.             ENDELSE
  498.             strData = CREATE_STRUCT(strData, szTag, value)
  499.         ENDFOR
  500.             
  501.         ; Create the vector of structures.
  502.         ReturnData = MAKE_ARRAY(iMax, VALUE = strData)
  503.             
  504.         ; Load the values into the vector of structures.
  505.         FOR i = 0, iMax - 1 DO BEGIN
  506.             FOR j = 0, jMax - 1 DO BEGIN
  507.                 IF (arrPointers[i, j] NE nullPtr) THEN BEGIN
  508.                     value = *(arrPointers[i, j])
  509.                 ENDIF ELSE BEGIN
  510.                     value = 0B
  511.                 ENDELSE
  512.  
  513.                 ; Compare type of data in array of pointers and the type of
  514.                 ; data in the structure.
  515.                 IF ((size(value))[1] NE (size(ReturnData[i].(j)))[1]) THEN BEGIN
  516.                     IF (fColMajor) THEN BEGIN
  517.                         MESSAGE, "Error - data in cell range specified " + $
  518.                             "is not a column major table.  Unable to " + $
  519.                             "create and return a vector of like-typed " + $
  520.                             "structures.", /CONTINUE
  521.                         GOTO, CleanUp                               
  522.                     ENDIF ELSE BEGIN
  523.                         MESSAGE, "Error - data in cell range specified " + $
  524.                             "is not a row major table.  Unable to " + $
  525.                             "create and return a vector of like-typed " + $
  526.                             "structures.", /CONTINUE
  527.                         GOTO, CleanUp
  528.                     ENDELSE
  529.                 ENDIF
  530.                 ReturnData[i].(j) = value
  531.             ENDFOR
  532.         ENDFOR
  533.     ENDELSE
  534.  
  535.     CleanUp: BEGIN
  536.         ; Clean up dynamically allocated memory.
  537.     IF (N_ELEMENTS(arrPointers) ne 0) THEN PTR_FREE, arrPointers
  538.         ; Free the file.
  539.         IF (lunInfile NE 0) THEN BEGIN
  540.             FREE_LUN, lunInfile
  541.         ENDIF
  542.     END
  543.  
  544.     RETURN, ReturnData
  545.  
  546. END
  547.